// Project name:JavaRedis
// Creator:muhong
// Date time:2023/7/31,3:18 下午
// Name:AbstractByteBuf

package io.gmh.buffer;

import io.gmh.util.IllegalReferenceCountException;
import org.gmh.channel.AbstractChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.gmh.util.internal.ObjectUtil.checkPositiveOrZero;

public abstract class AbstractByteBuf extends ByteBuf {

    private final static Logger logger = LoggerFactory.getLogger(AbstractChannelHandlerContext.class);

    private static final boolean checkBounds;

    static final boolean checkAccessible;


    static {
        checkAccessible = true;
        checkBounds = true;
    }

    int readerIndex;
    int writerIndex;

    private int markedReaderIndex;
    private int markedWriterIndex;
    private int maxCapacity;

    protected AbstractByteBuf(int maxCapacity) {
        checkPositiveOrZero(maxCapacity, "maxCapacity");
        this.maxCapacity = maxCapacity;
    }

    @Override
    public boolean isReadable() {
        return false;
    }

    @Override
    public ByteBuf asReadOnly() {
        return null;
    }

    @Override
    public int maxCapacity() {
        return maxCapacity;
    }

    protected final void maxCapacity(int maxCapacity) {
        this.maxCapacity = maxCapacity;
    }

    @Override
    public int readerIndex() {
        return readerIndex;
    }

    private static void checkIndexBounds(final int readerIndex, final int writerIndex, final int capacity) {
        if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
                    readerIndex, writerIndex, capacity));
        }
    }

    @Override
    public ByteBuf readerIndex(int readerIndex) {
        if (checkBounds) {
            checkIndexBounds(readerIndex, writerIndex, capacity());
        }
        this.readerIndex = readerIndex;
        return this;
    }

    @Override
    public int writerIndex() {
        return writerIndex;
    }

    @Override
    public ByteBuf writerIndex(int writerIndex) {
        if (checkBounds) {
            checkIndexBounds(this.readerIndex, writerIndex, capacity());
        }
        this.writerIndex = writerIndex;
        return this;
    }


    @Override
    public ByteBuf setIndex(int readerIndex, int writerIndex) {
        if (checkBounds) {
            checkIndexBounds(readerIndex, writerIndex, capacity());
        }
        setIndex0(readerIndex, writerIndex);
        return this;
    }

    final void setIndex0(int readerIndex, int writerIndex) {
        this.readerIndex = readerIndex;
        this.writerIndex = writerIndex;
    }

    @Override
    public ByteBuf clear() {
        readerIndex = writerIndex = 0;
        return this;
    }

    @Override
    public boolean isReadable(int size) {
        return writerIndex > readerIndex;
    }

    @Override
    public boolean isWritable() {
        return capacity() > writerIndex;
    }

    @Override
    public int readableBytes() {
        return writerIndex - readerIndex;
    }

    @Override
    public int writableBytes() {
        return capacity() - writerIndex;
    }

    @Override
    public int maxWritableBytes() {
        return maxCapacity() - writerIndex;
    }

    @Override
    public ByteBuf markReaderIndex() {
        markedReaderIndex = readerIndex;
        return this;
    }

    @Override
    public ByteBuf resetReaderIndex() {
        readerIndex(markedReaderIndex);
        return this;
    }

    @Override
    public ByteBuf markWriterIndex() {
        markedWriterIndex = writerIndex;
        return this;
    }

    @Override
    public ByteBuf resetWriterIndex() {
        writerIndex(markedWriterIndex);
        return this;
    }

    @Override
    public ByteBuf discardReadBytes() {
        if (readerIndex == 0) {
            ensureAccessible();
            return this;
        }
        if (readerIndex != writerIndex) {
            setBytes(0, this, readerIndex, writerIndex - readerIndex);
            writerIndex -= readerIndex;
            adjustMarkers(readerIndex);
            readerIndex = 0;
        }else {
            ensureAccessible();
            adjustMarkers(readerIndex);
            writerIndex = readerIndex = 0;
        }
        return this;
    }

    @Override
    public ByteBuf discardSomeReadBytes() {
        if (readerIndex >0){
            if (readerIndex == writerIndex){
                ensureAccessible();
                adjustMarkers(readerIndex);
                writerIndex = readerIndex = 0;
                return this;
            }
            if (readerIndex >= capacity() >>>1){
                setBytes(0, this, readerIndex, writerIndex - readerIndex);
                writerIndex -= readerIndex;
                adjustMarkers(readerIndex);
                readerIndex = 0;
                return this;
            }
        }
        ensureAccessible();
        return this;
    }

    protected final void adjustMarkers(int decrement){
        if (markedReaderIndex <= decrement){
            markedReaderIndex = 0;
            if (markedWriterIndex <= decrement){
                markedWriterIndex = 0;
            }else{
                markedWriterIndex -= decrement;
            }
        }else{
            markedReaderIndex -= decrement;
            markedWriterIndex -= decrement;
        }
    }

    protected final void ensureAccessible() {
        if (checkAccessible && !isAccessible()) {
            throw new IllegalReferenceCountException(0);
        }
    }
}
